package com.gmstu.springorm.framework;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.jdbc.core.RowMapper;

import javax.persistence.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author John.zhang
 * @version Id: EntityOperation, v 1.0 2020/9/2 15:39 ZhiYong Exp $
 */

@Slf4j
public class EntityOperation<T> {

    public Class<T> entityClass = null;

    public final Map<String, PropertyMapping> mappings;

    public final RowMapper<T> rowMapper;

    public final String tableName;

    public String allColumn = "*";

    public Field pkField;


    public EntityOperation(Class<T> clazz, String pk) throws Exception {
        if (!clazz.isAnnotationPresent(Entity.class)) {
            throw new Exception(clazz.getName() + "中未找到Entity注解，ORM无法映射");
        }
        this.entityClass = clazz;
        Table table = entityClass.getAnnotation(Table.class);
        if (table != null) {
            this.tableName = table.name();
        } else {
            this.tableName = entityClass.getSimpleName();
        }
        Map<String, Method> getters = ClassMappings.findPublicGetters(this.entityClass);
        Map<String, Method> setters = ClassMappings.findPublicSetters(this.entityClass);
        Field[] fields = ClassMappings.findFields(this.entityClass);
        fillPkFieldAndAllColumn(pk, fields);
        this.mappings = getPropertyMappings(getters, setters, fields);
        this.allColumn = this.mappings.keySet().toString().replace("[", "").replace("]", "").replace(" ", "");
        this.rowMapper = createRowMapper();
    }

    private RowMapper<T> createRowMapper() {
        return new RowMapper<T>() {
            @Override
            public T mapRow(ResultSet rs, int rowNum) throws SQLException {
                try {
                    T t = entityClass.newInstance();
                    ResultSetMetaData metaData = rs.getMetaData();
                    int columnCount = metaData.getColumnCount();
                    for (int i = 0; i < columnCount; i++) {
                        Object value = rs.getObject(i);
                        String columnName = metaData.getColumnName(i);
                        fillBeanFieldValue(t, columnName, value);
                    }
                    return null;
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        };
    }


    private Map<String, PropertyMapping> getPropertyMappings(Map<String, Method> getters, Map<String, Method> setters, Field[] fields) {
        Map<String, PropertyMapping> mappings = new HashMap<String, PropertyMapping>(16);
        String name;
        for (Field field : fields) {
            if (field.isAnnotationPresent(Transient.class)) {
                continue;
            }
            name = field.getName();
            if (name.startsWith("is")) {
                name = name.substring(2);
            }
            name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
            Method getter = getters.get(name);
            Method setter = setters.get(name);
            if (getter == null || setter == null) {
                continue;
            }
            Column column = field.getAnnotation(Column.class);
            if (column == null) {
                mappings.put(field.getName(), new PropertyMapping(getter, setter, field));
            } else {
                mappings.put(column.name(), new PropertyMapping(getter, setter, field));
            }
        }
        return mappings;
    }


    private void fillPkFieldAndAllColumn(String pk, Field[] fields) {

        if (!StringUtils.isEmpty(pk)) {
            try {
                pkField = entityClass.getDeclaredField(pk);
                pkField.setAccessible(true);
            } catch (Exception e) {
                log.error("没有找到主键列，主键列名必须余属性相同");
            }
        } else {
            for (Field field : fields) {
                Id id = field.getAnnotation(Id.class);
                if (id != null) {
                    pkField = field;
                    break;
                }
            }
        }
    }


    private void fillBeanFieldValue(T t, String columnName, Object value) {
        if (null != value) {
            PropertyMapping propertyMapping = mappings.get(columnName);
            if (null != propertyMapping) {
                propertyMapping.set(t, value);
            }
        }
    }


}
